//
//  Problem1237.swift
//  TestProject
//
//  Created by 武侠 on 2021/5/7.
//  Copyright © 2021 zhulong. All rights reserved.
//

import UIKit

/*
 1237. 找出给定方程的正整数解
 给你一个函数  f(x, y) 和一个目标结果 z，函数公式未知，请你计算方程 f(x,y) == z 所有可能的正整数 数对 x 和 y。满足条件的结果数对可以按任意顺序返回。

 尽管函数的具体式子未知，但它是单调递增函数，也就是说：

 f(x, y) < f(x + 1, y)
 f(x, y) < f(x, y + 1)
 函数接口定义如下：
     interface CustomFunction {
     public:
       // Returns some positive integer f(x, y) for two positive integers x and y based on a formula.
       int f(int x, int y);
     };
 你的解决方案将按如下规则进行评判：
     判题程序有一个由 CustomFunction 的 9 种实现组成的列表，以及一种为特定的 z 生成所有有效数对的答案的方法。
     判题程序接受两个输入：function_id（决定使用哪种实现测试你的代码）以及目标结果 z 。
     判题程序将会调用你实现的 findSolution 并将你的结果与答案进行比较。
     如果你的结果与答案相符，那么解决方案将被视作正确答案，即 Accepted 。
    
 示例 1：
     输入：function_id = 1, z = 5
     输出：[[1,4],[2,3],[3,2],[4,1]]
     解释：function_id = 1 暗含的函数式子为 f(x, y) = x + y
     以下 x 和 y 满足 f(x, y) 等于 5：
     x=1, y=4 -> f(1, 4) = 1 + 4 = 5
     x=2, y=3 -> f(2, 3) = 2 + 3 = 5
     x=3, y=2 -> f(3, 2) = 3 + 2 = 5
     x=4, y=1 -> f(4, 1) = 4 + 1 = 5
 示例 2：
     输入：function_id = 2, z = 5
     输出：[[1,5],[5,1]]
     解释：function_id = 2 暗含的函数式子为 f(x, y) = x * y
     以下 x 和 y 满足 f(x, y) 等于 5：
     x=1, y=5 -> f(1, 5) = 1 * 5 = 5
     x=5, y=1 -> f(5, 1) = 5 * 1 = 5

 提示：
     1 <= function_id <= 9
     1 <= z <= 100
     题目保证 f(x, y) == z 的解处于 1 <= x, y <= 1000 的范围内。
     在 1 <= x, y <= 1000 的前提下，题目保证 f(x, y) 是一个 32 位有符号整数。
 */
@objcMembers class Problem1237: NSObject {
    func solution() {
        let fun = CustomFunction()
        print(findSolutionPoint(fun, 5))
    }
    
    /*
     题意：不告诉你函数，让你用1 <= x, y <= 1000 一个一个的试，找到所有f(x,y)=z，
     请利用‘递增’和‘1 <= x, y <= 1000’这2个条件，找到最快的方法

     方法一：暴力破解
     */
    func findSolution(_ customfunction: CustomFunction, _ z: Int) -> [[Int]] {
        var result: [[Int]] = []
        var endY = 1000
        var tz = 0
        for x in 1...1000 {
            if endY < 1 {
                break
            }
            for y in 1...endY {
                tz = customfunction.f(x, y)
                if tz == z {
                    result.append([x, y])
                    endY = y - 1
                    break
                } else if tz > z {
                    break
                }
            }
        }
        return result
    }
    
    /*
     方法二：二分法
     在查找y的时候，使用二分法加快速度
     */
    func findSolutionEr(_ customfunction: CustomFunction, _ z: Int) -> [[Int]] {
        var result: [[Int]] = []
        var tz = 0
        
        // 二分法：
        var l = 1
        var r = 1000
        var mid = 0
        var lastRight = r
        
        for x in 1...1000 {
            l =  1
            r = lastRight
            while l <= r {
                mid = l + (r - l) >> 1
                tz = customfunction.f(x, mid)
                if tz == z {
                    result.append([x, mid])
                    lastRight = mid - 1
                    break
                } else if tz > z {
                    r = mid - 1
                } else {
                    l = mid + 1
                }
            }
            
        }
        return result
    }
    
    /*
     方法三：双指针
     利用单调性：创建2个指针，x指向1， y指向1000
     当f(x, y) == 1000时，根据单调递增：尝试：x += 1 y -= 1
     当f(x, y) > 1000时，根据单调递增：尝试：y -= 1
     当f(x, y) < 1000时，根据单调递减：尝试：x += 1
     */
    func findSolutionPoint(_ customfunction: CustomFunction, _ z: Int) -> [[Int]] {
        var result: [[Int]] = []
        var x = 1
        var y = 1000
        var tz = 0
        while x <= 1000, y >= 1 {
            tz = customfunction.f(x, y)
            if tz == z {
                result.append([x, y])
                x += 1
                y -= 1
            } else if tz > z {
                y -= 1
            } else {
                x += 1
            }
        }
        return result
    }
    
    class CustomFunction {
         // Returns f(x, y) for any given positive integers x and y.
         // Note that f(x, y) is increasing with respect to both x and y.
         // i.e. f(x, y) < f(x + 1, y), f(x, y) < f(x, y + 1)
         func f(_ x: Int, _ y: Int) -> Int {
            return x + y
         }
     }
}
